// ==========Tinymce.vue==========
<template>
  <div class="prefixCls" :style="{ width: containerWidth }">
    <textarea :id="tinymceId" ref="elRef" :style="{ visibility: 'hidden' }"></textarea>
  </div>
</template>

<script setup>
import tinymce from "tinymce/tinymce"
import "tinymce/themes/silver"
import "tinymce/icons/default/icons"
import "tinymce/plugins/advlist"
import "tinymce/plugins/anchor"
import "tinymce/plugins/autolink"
import "tinymce/plugins/code"
import "tinymce/plugins/codesample"
import "tinymce/plugins/directionality"
import "tinymce/plugins/fullscreen"
import "tinymce/plugins/hr"
import "tinymce/plugins/insertdatetime"
import "tinymce/plugins/link"
import "tinymce/plugins/lists"
import "tinymce/plugins/media"
import "tinymce/plugins/image" // 插入上传图片插件
import "tinymce/plugins/importcss" //图片工具
import "tinymce/plugins/imagetools" //图片工具
import "tinymce/plugins/charmap" // 特殊字符
import "tinymce/plugins/nonbreaking"
import "tinymce/plugins/noneditable"
import "tinymce/plugins/pagebreak"
import "tinymce/plugins/paste"
import "tinymce/plugins/preview"
import "tinymce/plugins/print"
import "tinymce/plugins/save"
import "tinymce/plugins/searchreplace"
import "tinymce/plugins/spellchecker"
import "tinymce/plugins/tabfocus"
import "tinymce/plugins/template"
import "tinymce/plugins/textpattern"
import "tinymce/plugins/visualblocks"
import "tinymce/plugins/visualchars"
import "tinymce/plugins/wordcount"
import "tinymce/plugins/table"

import { computed, nextTick, ref, unref, watch, onDeactivated, onBeforeUnmount, getCurrentInstance } from "vue"
import { toolbar, plugins } from "./tinymce"
import { bindHandlers } from "./helper"
import { upload } from "@/api/upload"
const props = defineProps({
  options: {
    type: Object,
    default: () => {}
  },
  value: {
    type: String
  },
  toolbar: {
    type: Array,
    default: toolbar
  },
  plugins: {
    type: Array,
    default: plugins
  },
  modelValue: {
    type: String
  },
  height: {
    type: [Number, String],
    required: false,
    default: 500
  },
  width: {
    type: [Number, String],
    required: false,
    default: "auto"
  },
  showImageUpload: {
    type: Boolean,
    default: true
  }
})
const emits = defineEmits(["change", "update:modelValue", "inited", "init-error"])
const { attrs } = getCurrentInstance()
const tinymceId = ref(buildShortUUID("tiny-vue"))
const containerWidth = computed(() => {
  const width = props.width
  if (isNumber(width)) {
    return `${width}px`
  }
  return width
})
const editorRef = ref(null)
const fullscreen = ref(false)
const elRef = ref(null)
const tinymceContent = computed(() => props.modelValue)

const initOptions = computed(() => {
  const { height, options, toolbar, plugins } = props
  const publicPath = "/"
  return {
    selector: `#${unref(tinymceId)}`,
    height,
    toolbar,
    menubar: "file edit insert view format table",
    plugins,
    language_url: "/resource/tinymce/langs/zh_CN.js",
    language: "zh_CN",
    branding: false,
    default_link_target: "_blank",
    link_title: false,
    object_resizing: true,
    auto_focus: true,
    skin: "oxide",
    paste_data_images: true,
    skin_url: "/resource/tinymce/skins/ui/oxide",
    content_css: "/resource/tinymce/skins/ui/oxide/content.min.css",
    urlconverter_callback: (url, node, onSave, name) => {
      if (node === "img" && url.startsWith("blob:")) {
        tinymce.activeEditor && tinymce.activeEditor.uploadImages()
      }
      // Return new URL
      return url
    },
    images_upload_handler: function (blobInfo, success, failure) {
      var file = blobInfo.blob()
      if (
        file.type !== "image/jpeg" &&
        file.type !== "image/png" &&
        file.type !== "image/gif" &&
        file.type !== "image/bmp" &&
        file.type !== "image/jpg"
      ) {
        failure("只能上传jpg、png、gif、bmp、jpeg格式的图片")
        return
      }
      upload(file)
        .then((res) => {
          success(import.meta.env.VITE_APP_BASE_API + res.fileName)
        })
        .catch(() => {
          failure("上传失败,请重试")
        })
    },
    file_picker_callback: function (callback, value, meta) {
      var filetype = ".pdf, .txt, .zip, .rar, .7z, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .mp3, .mp4"
      switch (meta.filetype) {
        case "image":
          filetype = ".jpg, .jpeg, .png, .gif, .bmp"
          break
        case "media":
          filetype = ".mp3, .mp4"
          break
        case "file":
        default:
      }
      //模拟出一个input用于添加本地文件
      var input = document.createElement("input")
      input.setAttribute("type", "file")
      input.setAttribute("accept", filetype)
      input.click()
      input.onchange = function () {
        var file = this.files[0]
        upload(file)
          .then((res) => {
            callback(import.meta.env.VITE_APP_BASE_API + res.fileName)
          })
          .catch(() => {
            callback("上传失败,请重试")
          })
      }
    },
    ...options,
    setup: (editor) => {
      editorRef.value = editor
      editor.on("init", (e) => initSetup(e))
    }
  }
})

const disabled = computed(() => {
  const { options } = props
  const getdDisabled = options && Reflect.get(options, "readonly")
  const editor = unref(editorRef)
  if (editor) {
    editor.setMode(getdDisabled ? "readonly" : "design")
  }
  return getdDisabled ?? false
})

watch(
  () => attrs.disabled,
  () => {
    const editor = unref(editorRef)
    if (!editor) {
      return
    }
    editor.setMode(attrs.disabled ? "readonly" : "design")
  }
)

onMountedOrActivated(() => {
  if (!initOptions.value.inline) {
    tinymceId.value = buildShortUUID("tiny-vue")
  }
  nextTick(() => {
    setTimeout(() => {
      initEditor()
    }, 30)
  })
})

onBeforeUnmount(() => {
  destory()
})

onDeactivated(() => {
  destory()
})

function destory() {
  if (tinymce !== null) {
    tinymce?.remove?.(unref(initOptions).selector)
  }
}

function initSetup(e) {
  const editor = unref(editorRef)
  if (!editor) {
    return
  }
  const value = props.modelValue || ""

  editor.setContent(value)
  bindModelHandlers(editor)
  bindHandlers(e, attrs, unref(editorRef))
}

function initEditor() {
  const el = unref(elRef)
  if (el) {
    el.style.visibility = ""
  }
  tinymce
    .init(unref(initOptions))
    .then((editor) => {
      emits("inited", editor)
    })
    .catch((err) => {
      emits("init-error", err)
    })
}

function setValue(editor, val, prevVal) {
  if (
    editor &&
    typeof val === "string" &&
    val !== prevVal &&
    val !== editor.getContent({ format: attrs.outputFormat })
  ) {
    editor.setContent(val)
  }
}

function bindModelHandlers(editor) {
  const modelEvents = attrs.modelEvents ? attrs.modelEvents : null
  const normalizedEvents = Array.isArray(modelEvents) ? modelEvents.join(" ") : modelEvents

  watch(
    () => props.modelValue,
    (val, prevVal) => {
      setValue(editor, val, prevVal)
    }
  )

  watch(
    () => props.value,
    (val, prevVal) => {
      setValue(editor, val, prevVal)
    },
    {
      immediate: true
    }
  )

  editor.on(normalizedEvents || "change keyup undo redo", () => {
    const content = editor.getContent({ format: attrs.outputFormat })
    emits("update:modelValue", content)
    emits("change", content)
  })

  editor.on("FullscreenStateChanged", (e) => {
    fullscreen.value = e.state
  })
}

function handleImageUploading(name) {
  const editor = unref(editorRef)
  if (!editor) {
    return
  }
  editor.execCommand("mceInsertContent", false, getUploadingImgName(name))
  const content = editor?.getContent() ?? ""
  setValue(editor, content)
}

function handleDone(name, url) {
  const editor = unref(editorRef)
  if (!editor) {
    return
  }
  const content = editor?.getContent() ?? ""
  const val = content?.replace(getUploadingImgName(name), `<img src="${url}"/>`) ?? ""
  setValue(editor, val)
}

function getUploadingImgName(name) {
  return `[uploading:${name}]`
}

// ==== isNumber  函数====
const toString = Object.prototype.toString
function is(val, type) {
  return toString.call(val) === `[object ${type}]`
}
function isNumber(val) {
  return is(val, "Number")
}

// ==== buildShortUUID  函数====
function buildShortUUID(prefix = "") {
  const time = Date.now()
  const random = Math.floor(Math.random() * 1000000000)
  return prefix + "_" + random + String(time)
}

// ==== onMountedOrActivated  hook====
import { onMounted, onActivated } from "vue"
function onMountedOrActivated(hook) {
  let mounted
  onMounted(() => {
    hook()
    nextTick(() => {
      mounted = true
    })
  })
  onActivated(() => {
    if (mounted) {
      hook()
    }
  })
}
</script>

<style lang="scss" scoped>
.prefixCls {
  position: relative;
  line-height: normal;
}
textarea {
  z-index: -1;
  visibility: hidden;
}
</style>
